/*
 *所有关于Bas_BarcodeRule类的业务代码应在此处编写
*可使用repository.调用常用方法，获取EF/Dapper等信息
*如果需要事务请使用repository.DbContextBeginTransaction
*也可使用DBServerProvider.手动获取数据库相关信息
*用户信息、权限、角色等使用UserContext.Current操作
*Bas_BarcodeRuleService对增、删、改查、导入、导出、审核业务代码扩展参照ServiceFunFilter
*/
using DGZImp.Core.BaseProvider;
using DGZImp.Core.Extensions.AutofacManager;
using DGZImp.BasProject.DomainModels;
using System.Linq;
using DGZImp.Core.Utilities;
using System.Linq.Expressions;
using DGZImp.Core.Extensions;

using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Http;
using DGZImp.BasProject.IRepositories;
using DGZImp.Core.Exceptions;
using DGZImp.Core.DbSqlSugar;
using DGZImp.WmsProject.Dtos;
using Newtonsoft.Json.Linq;
using System.Net;

namespace DGZImp.BasProject.Services
{
    public partial class Bas_BarcodeRuleService
    {
        private readonly IHttpContextAccessor _httpContextAccessor;
        private readonly IBas_BarcodeRuleRepository _repository;//访问数据库
        WebResponseContent webResponse = new WebResponseContent();

        [ActivatorUtilitiesConstructor]
        public Bas_BarcodeRuleService(
            IBas_BarcodeRuleRepository dbRepository,
            IHttpContextAccessor httpContextAccessor
            )
        : base(dbRepository)
        {
            _httpContextAccessor = httpContextAccessor;
            _repository = dbRepository;
            //多租户会用到这init代码，其他情况可以不用
            //base.Init(dbRepository);
        }


        /// <summary>
        /// 新建
        /// </summary>
        /// <param name="saveDataModel"></param>
        /// <returns></returns>
        public override WebResponseContent Add(SaveModel saveDataModel)
        {
            // 在保存数据库前的操作，所有数据都验证通过了，这一步执行完就执行数据库保存
            AddOnExecuting = (Bas_BarcodeRule data, object list) =>
            {
                var exist = _repository.Exists(x => x.RuleType == data.RuleType && x.IsValid == 1);

                if (exist) return webResponse.Error("已经存在相同的条码类型");

                return webResponse.OK();
            };


            return base.Add(saveDataModel);
        }

        /// <summary>
        /// 编辑操作
        /// </summary>
        /// <param name="saveModel"></param>
        /// <returns></returns>
        public override WebResponseContent Update(SaveModel saveModel)
        {

            //编辑方法保存数据库前处理
            UpdateOnExecuting = (Bas_BarcodeRule data, object addList, object updateList, List<object> delKeys) =>
            {

                var exist = _repository.Exists(x => x.RuleType == data.RuleType && x.IsValid == 1 && x.BarcodeRuleId != data.BarcodeRuleId);

                if (exist) return webResponse.Error("已经存在相同的条码类型");

                return webResponse.OK();
            };

            return base.Update(saveModel);
        }

        public async Task<ReqGetBarcodeRuleId> GetBarcodeRuleId(ReqGetRuleId model)
        {
            if (model.IsCommon == null || model.Type == null) throw new AppException("查找条码规则是否通用,标签类型不能为空");
            var res = new List<ReqGetBarcodeRuleId>() { };
            if (model.IsCommon == 0)
            {
                //非通用的情况
                /* if (string.IsNullOrEmpty(model.GroupId) || string.IsNullOrEmpty(model.MaterialId))
                    throw new AppException("查找条码规则工序,客户不能为空");
                if (model.Type == 3)
                {
                    if (model.Level.IsNullOrEmpty()) throw new AppException("查找条码规则包装序号，层级不能为空。");
                }
                // 拉取当前工序下所有的标签规则
                var groupRuleList = await _repository.DbContext.Queryable<Bas_BarcodeRule>()
                .LeftJoin<CBarcodeRuleUsageT>((a, b) => b.barcode_rule_id == a.id)
                .Where((a, b) => b.group_id == model.GroupId)
                .WhereIF(model.Type == 3, (a, b) => a.level == model.Level)
                .Where((a, b) => a.is_valid == "Y" && b.is_valid == "Y")
                .Select((a, b) => new ReqGetBarcodeRuleId
                {
                    BarcodeRuleId = a.id,
                    RuleCode = a.barcode_rule_no,
                    RuleName = a.rule_name,
                    RuleDesc = a.rule_description,
                    SplitChar = a.split_char,
                    RuleType = a.type,
                    Level = a.level,
                    IsCommon = a.is_common,
                    Remarks = a.Remarks,
                    cust_id = b.cust_id,
                    sku_id = b.sku_id
                })
                .ToListAsync();
                //if (groupRuleList.Count < 1) throw new AppException("当前工站未找到条码规则");
                //先确认当前客户是否存在特殊设置，若存在
                res = groupRuleList.Where(a => a.cust_id == model.CustId && a.sku_id == model.MaterialId).ToList();
                if (res.Count < 1)
                    //cust_id=null + sku_id 判断物料是否存在通用
                    res = groupRuleList.Where(a => string.IsNullOrEmpty(a.cust_id) && a.sku_id == model.MaterialId).ToList();
                if (res.Count < 1)
                    // cust_id + sku_id=null,判断客户是否存在通用设置
                    res = groupRuleList.Where(a => a.cust_id == model.CustId && string.IsNullOrEmpty(a.sku_id)).ToList();
                if (res.Count < 1)
                    //cust_id=null +sku_id=null,判断通用标签设置
                    res = groupRuleList.Where(a => string.IsNullOrEmpty(a.cust_id) && string.IsNullOrEmpty(a.sku_id)).ToList();
                //查找通用标签
                if (res.Count < 1)
                {
                    res = await _repository.DbContext.Queryable<Bas_BarcodeRule>()
                     .Where(a => a.isco == 1)
                     .Where(a => a.type == model.type)
                     .WhereIF(model.type == 3, a => a.level == model.level)
                     .Where(a => a.is_valid == "Y")
                     .Select(a => new ReqGetBarcodeRuleId
                     {
                         id = a.id,
                         barcode_rule_no = a.barcode_rule_no,
                         rule_name = a.rule_name,
                         rule_description = a.rule_description,
                         split_char = a.split_char,
                         type = a.type,
                         level = a.level,
                         is_common = a.is_common,
                         remarks = a.remarks,
                     })
                     .ToListAsync();
                }*/
            }
            else
            {
                res = await _repository.DbContext.Queryable<Bas_BarcodeRule>()
                    .Where(a => a.RuleType == model.Type)
                    .Where(a => a.IsCommon == 1)
                    .WhereIF(model.Type == 3, a => a.Level == model.Level)
                    .Where(a => a.IsValid == 1)
                    .Select(a => new ReqGetBarcodeRuleId
                    {
                        BarcodeRuleId = a.BarcodeRuleId,
                        RuleCode = a.RuleCode,
                        RuleName = a.RuleName,
                        RuleDesc = a.RuleDesc,
                        SplitChar = a.SplitChar,
                        RuleType = a.RuleType,
                        Level = a.Level,
                        IsCommon = a.IsCommon,
                        Remarks = a.Remarks,
                    })
                    .ToListAsync();

            }

            if (res.Count > 1) throw new AppException("找到了多个条码规则，请确认。");
            if (res.Count <= 0) throw new AppException("未找到条码规则。");


            return res[0];
        }


        public JObject GenerateBarcode(string model)
        {
            JObject vmodel = JObject.Parse(model);
            var rule_id = (long)vmodel["RuleId"];
            JObject data = JObject.FromObject(vmodel["Data"]);
            var barcode_level = (string)vmodel["barcode_level"];
            //获取条码规则
            var rule = _repository.DbContext.DoGetById<Bas_BarcodeRule>(rule_id);
            if (rule == null) throw new AppException("条码规则不正确");
            //获取条码规则明细数据
            var list = _repository.DbContext.Queryable<Bas_BarcodeRuleDetail, Bas_BarcodeElement>((t, f) => t.BarcodeElementId == f.BarcodeElementId)
                .Where(t => t.BarcodeRuleId == rule_id)
                .Where((t, f) => t.IsValid == 1 && f.IsValid == 1)
                .OrderBy(t => t.Seq, OrderByType.Desc)
                .Select((t, f) => new
                {
                    t.BarcodeRuleDetailId,
                    t.Seq,
                    t.FieldValue,
                    t.ResetFlag, //流水号重置标识
                    t.ResetStartValue, //流水号起始值
                    f.CodingType,
                    f.CodingParam,
                    f.CodingFormula,
                    f.CodingLength,
                    f.PadLeftChar,
                    f.Metric,
                    f.MetricSpecialString,
                    f.SampleValue,
                }).ToList();

            //有流水号的话，做一些检查
            var serialItems = list.Where(i => i.CodingType == "SerialNumber").ToList();
            if (serialItems.Count > 1) throw new AppException("条码规则不能包含多个流水号");
            if (serialItems.Count > 0 && list.Where(i => i.ResetFlag == 1).ToList().Count == 0)
                throw new AppException("条码规则没有包含流水号的重置条件");

            var resList = new List<dynamic>();
            //逐一处理
            foreach (var a in list)
            {
                string res = "";
                try
                {
                    switch (a.CodingType)
                    {
                        case "Datetime":
                            var formula = (string)a.CodingFormula;
                            res = ConvertSpecialDateCode(formula);
                            break;
                        case "String":
                            res = a.SampleValue;
                            break;
                        case "View":
                            if (!data.ContainsKey(a.CodingFormula)) throw new AppException(string.Format("视图[{0}]的主键[{1}]未传入，请与系统管理员确认", a.CodingParam, a.CodingFormula));
                            var tmpRes = GetViewData(a.CodingParam, a.CodingFormula, (string)data[a.CodingFormula], a.FieldValue);
                            //if (!tmpRes.Status) return tmpRes;
                            if (tmpRes != null)
                                // return Res.Error<JObject>(String.Format("视图[{0}]的主键[{1}]数据不唯一，请确认..", a.coding_param, a.coding_formula));
                                res = (string)tmpRes;
                            break;
                        case "Param":
                            if (!data.ContainsKey(a.FieldValue)) throw new AppException(string.Format("条码参数 {0} 未传入，请与系统管理员确认", a.FieldValue));
                            res = (string)data[a.FieldValue];
                            break;
                        case "SerialNumber":
                            res = "0";
                            break;
                    }
                    if (a.CodingType != "SerialNumber")
                    {
                        //先做进制转换，进制转换要求值必须为整数
                        if (a.Metric == 1)
                        {
                            try
                            {
                                long m = Convert.ToInt64(res);
                                res = NumberFormat(m, a.MetricSpecialString);
                            }
                            catch (Exception)
                            {
                                throw new Exception("进制转换出错");
                            }
                        }
                        //再做字符串补齐
                        res = StringPadLeft(res, a.CodingLength == null ? 0 : (int)a.CodingLength, a.PadLeftChar);
                        resList.Add(new { value = res, a.CodingType, a.ResetFlag });
                    }
                    else
                    {
                        resList.Add(new { value = res, a.CodingType, ResetFlag = 0 });
                    }
                    if (string.IsNullOrEmpty(res)) throw new AppException(string.Format("条码要素 {0} 对应的值为空！", a.CodingParam));
                }
                catch (Exception e)
                {
                    throw new AppException(string.Format("条码要素 {0} 生成数据错误！详情：{1}", a.CodingParam, e.Message));
                }
            }
            //最后处理流水号
            int sn = 0;
            string snStr = "";
            if (serialItems.Count == 1)
            {
                var serialItem = serialItems.FirstOrDefault();
                //先组合出重置条件
                var tmp = resList.Select(t => t.ResetFlag == 1 ? (string)t.value : "%").ToArray();
                var reset_condition = string.Join(rule.SplitChar, tmp);
                //查找当前序号
                var cur = _repository.DbContext.Queryable<Bas_BarcodeSerial>()
                    .Where(t => t.BarcodeRuleId == rule_id && t.ResetCondition == reset_condition)
                    .OrderBy(t => t.CurrentSerialNum, OrderByType.Desc).First();
                //事务处理 先加锁
                bool inTrans = _repository.SqlSugarClient.Ado.Transaction != null;
                if (!inTrans) _repository.SqlSugarClient.Ado.BeginTran();
                try
                {
                    if (cur != null)
                    {
                        _repository.DbContext.Updateable<Bas_BarcodeSerial>()
                            .SetColumns(a => a.CurrentSerialNum == a.CurrentSerialNum + 1)
                            .Where(a => a.BarcodeSerialId == cur.BarcodeSerialId).ExecuteCommand();

                        sn = (int)(_repository.DbContext.DoGetById<Bas_BarcodeSerial>(cur.BarcodeSerialId)).CurrentSerialNum;
                    }
                    else
                    {
                        sn = Convert.ToInt32(serialItem.ResetStartValue);
                        _repository.DbContext.DoInsert<Bas_BarcodeSerial>(new Bas_BarcodeSerial
                        {
                            CurrentSerialNum = sn,
                            BarcodeRuleId = rule_id,
                            ResetCondition = reset_condition
                        });
                    }
                    if (!inTrans) _repository.SqlSugarClient.Ado.CommitTran();

                }
                catch (Exception e)
                {
                    if (!inTrans) _repository.SqlSugarClient.Ado.RollbackTran();
                    throw new AppException("流水号生成失败！详情：" + e.Message);
                }
                //进制转换
                if (serialItem.Metric == 1)
                {
                    try
                    {
                        snStr = NumberFormat(sn, serialItem.MetricSpecialString);
                    }
                    catch (Exception)
                    {
                        throw new AppException("生成条码时，进制转换出错");
                    }
                }
                else
                {
                    snStr = sn.ToString();
                }
                //再做字符串补齐
                snStr = StringPadLeft(snStr, (int)serialItem.CodingLength, serialItem.PadLeftChar);
            }

            if (rule.SplitChar.IsValuable())
            {
                var check = resList.Where(t => ((string)Convert.ToString(t.value)).Contains(rule.SplitChar)).FirstOrDefault();
                if (check != null)
                {
                    throw new AppException($"生成条码失败，条码要素：{check.CodingType}，值内容：{check.value}，不能包含条码分隔符：{rule.SplitChar}");
                }
            }

            var resListTmp = resList.Select(t => t.CodingType == "SerialNumber" ? snStr : t.value).ToArray();
            string barcode = string.Join(rule.SplitChar, resListTmp);
            JObject result = new JObject
            {
                ["barcode_level"] = barcode_level,
                ["barcode"] = barcode,
                ["index_number"] = snStr
            };
            return result;
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="formula"></param>
        /// <returns></returns>
        private static string ConvertSpecialDateCode(string formula)
        {
            string tmpRes;
            if (formula.ToUpper().Equals("Y"))//年最后一码
            {
                tmpRes = DateTime.Now.ToString("yyyy");
                tmpRes = tmpRes.Substring(tmpRes.Length - 1, 1);
            }
            else if (formula.ToUpper().Equals("DAYOFYEAR"))//年度天数
                tmpRes = DateTime.Now.DayOfYear.ToString();
            else if (formula.ToUpper().Equals("WEEKOFYEAR"))//年度周别
            {
                //处理年周
                var dayYear = DateTime.Now.DayOfYear;
                tmpRes = (dayYear / 7 + 1).ToString();
            }
            else if (formula.ToUpper().Equals("DAYOFWEEK"))//星期几
            {
                tmpRes = DateTime.Now.DayOfWeek.ToString();
            }
            else
                tmpRes = DateTime.Now.ToString(formula);

            return tmpRes;
        }

        /// <summary>
        /// 补齐字符串至指定长度，在左侧添加指定的字符
        /// </summary>
        /// <param name="str"></param>
        /// <param name="len"></param>
        /// <param name="t"></param>
        /// <returns></returns>
        public static string StringPadLeft(string str, int len, string t)
        {
            if (len <= 0) return str;
            if (t.Length != 1) throw new Exception("补齐字符有且只能有一个");
            if (str.Length > len) throw new Exception("数值超出指定长度");
            return str.PadLeft(len, Convert.ToChar(t));
        }

        /// <summary>
        /// 整数的进制转换，需要传入进制字符串
        /// </summary>
        /// <param name="n"></param>
        /// <param name="format"></param>
        /// <returns></returns>
        public static string NumberFormat(long n, string format)
        {
            if (string.IsNullOrEmpty(format)) return "";
            char[] strs = format.ToCharArray();
            int i = strs.Length;
            string res = "";
            while (n >= i)
            {
                long y = n % i;
                res = strs[y] + res;
                n = n / i;
            }
            res = strs[n] + res;
            return res;
        }

        /// <summary>
        /// 获取View的数据
        /// </summary>
        /// <param name="viewName">视图名</param>
        /// <param name="key">主键字段</param>
        /// <param name="val">主键值</param>
        /// <param name="col">查询字段</param>
        /// <returns></returns>
        public dynamic GetViewData(string viewName, string key, string val, string col = null)
        {
            if (string.IsNullOrEmpty(viewName) || string.IsNullOrEmpty(key) || string.IsNullOrEmpty(val) || string.IsNullOrEmpty(col)) throw new Exception("视图名称/主键/主键值/取值字段 均不能为空");
            JObject a = this.GetViewRow(viewName, key, val);
            return a[col];
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="viewName"></param>
        /// <param name="key"></param>
        /// <param name="val"></param>
        /// <returns></returns>
        public JObject GetViewRow(string viewName, string key, string val)
        {
            var tmp = this._repository.SqlSugarClient.Ado.SqlQuery<dynamic>("select * from " + viewName + " where " + key + " = @val", new { val });
            if (tmp.Count == 0) throw new AppException("未查询到有效的视图数据");
            if (tmp.Count > 1) throw new AppException("视图数据查询结果不唯一");
            JObject a = JObject.FromObject(tmp.FirstOrDefault());
            return a;
        }

    }
}
